
#include "metal/device.h"
#include "metal/sys.h"
#include "openamp/open_amp.h"
#include "stdio.h"
#include <rtthread.h>

#define SHM_DEVICE_NAME    "rtshm"

#define DEFAULT_PAGE_SHIFT (-1U)
#define DEFAULT_PAGE_MASK  (-1U)

#ifndef SHM_START_ADDR
#define SHM_START_ADDR AMP_SHARE_MEMORY_ADDRESS
#endif /* SHM_START_ADDR */

#ifndef SHM_SIZE
#define SHM_SIZE AMP_SHARE_MEMORY_SIZE
#endif /* SHM_SIZE */

static struct metal_io_region *shm_io;
static struct metal_device     shm_device =
{
    .name = SHM_DEVICE_NAME,
    .bus = NULL,
    .num_regions = 1,
    .node = {NULL},
    .irq_num = 0,
    .irq_info = NULL
};

void log_handler(enum metal_log_level level, const char *format, ...)
{
#ifdef DEFAULT_LOGGER_ON
    char                     msg[1024];
    va_list                  args;
    static const char *const level_strs[] = {
        "metal: emergency: ", "metal: alert:     ", "metal: critical:  ", "metal: error:     ",
        "metal: warning:   ", "metal: notice:    ", "metal: info:      ", "metal: debug:     ",
    };

    va_start(args, format);
    vsnprintf(msg, sizeof(msg), format, args);
    va_end(args);

    if(level <= METAL_LOG_EMERGENCY || level > METAL_LOG_DEBUG)
        level = METAL_LOG_EMERGENCY;

    fprintf(stderr, "%s%s", level_strs[level], msg);
#else
    (void)level;
    (void)format;
#endif
}

static int openamp_shmem_init(void)
{
    int                      status = 0;
    struct metal_device     *device;
    struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
    static metal_phys_addr_t shm_physmap;

    metal_params.log_handler = log_handler;
    metal_params.log_level   = METAL_LOG_DEBUG;

#if defined(RT_AMP_MASTER) && defined(RT_USING_AMP)
    rt_memset((void *)SHM_START_ADDR, 0, SHM_SIZE);
#endif

    metal_init(&metal_params);

    status = metal_register_generic_device(&shm_device);
    if(status != 0)
    {
        return status;
    }

    status = metal_device_open("generic", SHM_DEVICE_NAME, &device);
    if(status != 0)
    {
        return status;
    }

    shm_physmap = SHM_START_ADDR;
    metal_io_init(&device->regions[0], (void *)SHM_START_ADDR, &shm_physmap, SHM_SIZE, DEFAULT_PAGE_SHIFT, 0, NULL);

    shm_io = metal_device_io_region(device, 0);
    if(shm_io == NULL)
    {
        return -1;
    }

    return 0;
}
INIT_COMPONENT_EXPORT(openamp_shmem_init);

void openamp_shmem_deinit(void)
{
    metal_finish();
}

struct metal_io_region *openamp_shmemio_get(void)
{
    return shm_io;
}
